/*
*    Phantom v2.0 GUI Automation Software
*    Copyright (C) 2009  John C. Van Arsdall (john@phantomtest.com)
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License along
*    with this program; if not, write to the Free Software Foundation, Inc.,
*    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
// MinionLib.cpp : Defines the initialization routines for the DLL.
//

#include "stdafx.h"
#include "minionlib.h"
#include "minionmanager.h"

#include "ssi_scriptapi.h"

//#ifdef _DEBUG
//#define new DEBUG_NEW
//#endif

#pragma data_seg("Shared")
HHOOK g_hHook = SSI_NULL;
DWORD g_dwThreadIdCaller = 0;
int bALL_DATA_RECEIVED = 0;
int bMINION_MSG_FLAGS = 0;
int bMINION_SYNC = 0;
#pragma data_seg()
#pragma comment(linker, "/section:Shared,rws")

HINSTANCE g_hinstDll = SSI_NULL;
CMinionManager g_Manager;

void MinionLog(const char* Msg){
#if(0)
	printf("MinionLog: %s\n", Msg);
#endif
}

// THESE ARE LOCAL TO DLL


HHOOK GetGlobalHook(){
	return g_hHook;
}

DWORD GetGlobalCallerThread(){
	return g_dwThreadIdCaller;
}

SetMinionMsgFlags(int NEW_TYPE){
	if(bMINION_MSG_FLAGS & FLG_FUNC_TYPE){
		return 0;
	}
	bMINION_MSG_FLAGS = bMINION_MSG_FLAGS | NEW_TYPE;
	return 1;
}

void SetMinionSync(int NEW){
	bMINION_SYNC = NEW;	
}

CMinionManager* GetGlobalManager(){
	return &g_Manager;
}


// OK, THE ONLY THING THAT IS CUSTOM IN THIS WHOLE MESS IS THE
// GET MESSAGE PROC.  IT IS SET IN THE HOOK, AND CALLED WHEN A
// MESSAGE IS SENT.  FROM THIS, THE FUNCTION IS PROCESSED AND THE
// RETURN VALUE IS FORMED.  THEREFORE, THE CALLING APP MUST LINK TO
// THIS DLL TO *ONLY* GET A REFERENCE TO THE MINION MANAGER AND
// THE DATA MAP / MEMORY MAP FIELDS.  THE MINION MANAGER IS INITIALIZED
// WITH A POINTER SPECIFIC TO THE GET MSG PROC, WHICH IS LOCATED IN
// ANOTHER DLL?  BUT WAIT, DONT BOTH DLLS THEN HAVE TO BE INJECTED?
// NO, ONLY THE CUSTOM DLL NEEDS TO BE INJECTED, AS IT WILL LOAD
// THE MINION MANAGER DLL BECAUSE IT WILL BE LINKED TO IT. SOOO,
// THE CALLER MUST LINK TO THE CUSTOM DLL *AND* THE MINION MANAGER
// DLL, AND THE CUSTOM DLL MUST LINK TO THE MINION MANAGER DLL.


//
//	Note!
//
//		If this DLL is dynamically linked against the MFC
//		DLLs, any functions exported from this DLL which
//		call into MFC must have the AFX_MANAGE_STATE macro
//		added at the very beginning of the function.
//
//		For example:
//
//		extern "C" BOOL PASCAL EXPORT ExportedFunction()
//		{
//			AFX_MANAGE_STATE(AfxGetStaticModuleState());
//			// normal function body here
//		}
//
//		It is very important that this macro appear in each
//		function, prior to any calls into MFC.  This means that
//		it must appear as the first statement within the 
//		function, even before any object variable declarations
//		as their constructors may generate calls into the MFC
//		DLL.
//
//		Please see MFC Technical Notes 33 and 58 for additional
//		details.
//

// CMinionLibApp

/*
BEGIN_MESSAGE_MAP(CMinionLibApp, CWinApp)
END_MESSAGE_MAP()


// CMinionLibApp construction

CMinionLibApp::CMinionLibApp()
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance
}


// The one and only CMinionLibApp object

CMinionLibApp theApp;


// CMinionLibApp initialization

BOOL CMinionLibApp::InitInstance()
{
	CWinApp::InitInstance();
	g_hinstDll = AfxGetInstanceHandle();
	return TRUE;
}
*/

//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////

int SetMinionHook(DWORD dwThreadId, HOOKPROC InProc)
{
	int ctr = 0;

	// Create the Hook
	if(InProc){
		// If the global hook was never set, return false
		if(g_hHook != SSI_NULL){
			//MinionLog("ghook!=null");
			return 0;
		}

		// Get the current thread, which is the Caller's since this is called from the Caller
		g_dwThreadIdCaller = GetCurrentThreadId(); 

		// Set the hook, with the GetMsgProc, and the *remote* window thread's ID
		g_hHook = SetWindowsHookEx(WH_GETMESSAGE, InProc, g_hinstDll, dwThreadId);

		if(g_hHook == SSI_NULL){
			DWORD dwErr = GetLastError();
			return 0;
		}
    // Release the Hook
	}else{
		if(g_hHook == SSI_NULL){
			return 0;
		}

//		printf("JM: Releasing Minion Hook\n");
//		Sleep(2000);

		if(!UnhookWindowsHookEx(g_hHook)){
			DWORD dwErr = GetLastError();
			//String s;
			//s.Format("Error: %d", dwErr);
			//MinionLog(s.Str());
		}

		
//		printf("JM: Minion Hook Released\n");
//		Sleep(500);

		g_hHook = 0;
	}
	return 1;
}

void ClearMinionSync(){
	bMINION_SYNC = 0;
	bALL_DATA_RECEIVED = 0;
	bMINION_MSG_FLAGS = 0;  // Clear all flags
}

int WaitForSync(int Count){
	int ctr = 0;
	while(!bMINION_SYNC){
		Sleep(10);
		ctr++;
		if(ctr > Count){
			return 0;
		}
	}
	//MinionLog("Synced...");
	return 1;
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

LRESULT MinionMsgProc(int nCode, WPARAM wParam, LPARAM lParam){
	MinionLog("Processing Data");
	printf("%d, %d\n", bMINION_MSG_FLAGS, wParam);
	
	if(wParam == MINION_SETINTTYPE && !(bMINION_MSG_FLAGS & FLG_INT_TYPE)){
		MinionLog("int type received");
		SSI_INT64 Type = (SSI_INT64)(lParam);
		g_Manager.AddDataMap(Type, CDataInt::GetRegisteredType());
		bALL_DATA_RECEIVED++;
		bMINION_MSG_FLAGS = bMINION_MSG_FLAGS | FLG_INT_TYPE;
		return 0;
	}else if(wParam == MINION_SETSTRINGTYPE && !(bMINION_MSG_FLAGS & FLG_STRING_TYPE)){
		MinionLog("string type received");
		SSI_INT64 Type = (SSI_INT64)(lParam);
		g_Manager.AddDataMap(Type, CDataString::GetRegisteredType());
		bALL_DATA_RECEIVED++;
		bMINION_MSG_FLAGS = bMINION_MSG_FLAGS | FLG_STRING_TYPE;
		return 0;
	}else if(wParam == MINION_SETREALTYPE && !(bMINION_MSG_FLAGS & FLG_REAL_TYPE)){
		MinionLog("real type received");
		SSI_INT64 Type = (SSI_INT64)(lParam);
		g_Manager.AddDataMap(Type, CDataReal::GetRegisteredType());
		bALL_DATA_RECEIVED++;
		bMINION_MSG_FLAGS = bMINION_MSG_FLAGS | FLG_REAL_TYPE;
		return 0;
	}else if(wParam == MINION_SETBOOLTYPE && !(bMINION_MSG_FLAGS & FLG_BOOL_TYPE)){
		MinionLog("bool type received");
		String s;
		SSI_INT64 Type = (SSI_INT64)(lParam);
		g_Manager.AddDataMap(Type, CDataBool::GetRegisteredType());
		bALL_DATA_RECEIVED++;
		s.Format("COUNT: %d", bALL_DATA_RECEIVED);
		MinionLog(s.Str());
		bMINION_MSG_FLAGS = bMINION_MSG_FLAGS | FLG_BOOL_TYPE;
		return 0;
	}else if(wParam == MINION_SETVOIDTYPE && !(bMINION_MSG_FLAGS & FLG_VOID_TYPE)){
		MinionLog("void type received");
		SSI_INT64 Type = (SSI_INT64)(lParam);
		g_Manager.AddDataMap(Type, CDataVoid::GetRegisteredType());
		bALL_DATA_RECEIVED++;
		bMINION_MSG_FLAGS = bMINION_MSG_FLAGS | FLG_VOID_TYPE;
		return 0;
	}else{
		return CallNextHookEx(g_hHook, nCode, wParam, lParam);
	}
}

// THIS IS A CUSTOM FUNCTION ---------------------------------------
int ReadAPI(SSI_FunctionAPI& RetAPI, String& InClass, String& InExtra, CMinionManager* Mgr){
	
	MinionLog("READ API\n");
  MinionLog("A\n");

	if(!Mgr){
		return 0;
	}
	MinionLog("B\n");

	if(!Mgr->MapPtr){
		return 0;
	}
	MinionLog("C\n");
	
	// Should probably have an 'out'
	int ictr = 0;
	while(bALL_DATA_RECEIVED < 5){
		Sleep(10);
		ictr++;
		if(ictr > 1000){
			MinionLog("DATA NOT RECEIVED\n");
			return 0;
		}
		//MinionLog("Sleeping...");
	}
	MinionLog("DATA RECIEVED API\n");

	//if(bALL_DATA_RECEIVED == 5){
	//	MinionLog("All Data Here");
	//}else if(bALL_DATA_RECEIVED > 5){
	//	MinionLog("Greater Data Here");
	//}else{
	//	MinionLog("Less Data Here");
	//}

	CVariable* theVar = new CVariable;
	RetAPI.SetReturnData(theVar);

	CDataString* ClassData;
	CDataString* ExtraData;
	SSI_INT64 BytesRead = 0;
	
	MinionLog("API Reading Class\n");
	ClassData = (CDataString*)Mgr->ReadDataObject();
	if(!ClassData){
		delete theVar;
		return 0;
	}

	MinionLog("API Reading Extra\n");
	ExtraData = (CDataString*)Mgr->ReadDataObject();
	if(!ExtraData){
		delete theVar;
		return 0;
	}
	MinionLog(ExtraData->Data.Str());
	
	MinionLog("API Reading API\n");
	if(!Mgr->ReadFunctionAPI(RetAPI)){
		delete theVar;
		return 0;
	}

	MinionLog("API Read COMPLETE\n");

	//aMap.UnMap(aptr);

	InExtra = ExtraData->Data;
	InClass = ClassData->Data;

	delete ClassData;
	ClassData = 0;
	delete ExtraData;
	ExtraData = 0;
	delete theVar;

	return 1;

}

void SetModuleHandle(HINSTANCE Hnd){
	g_hinstDll = Hnd;
}
